package com.huwaiwai001.aqs;

import sun.misc.Unsafe;

import java.io.Serializable;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;
import java.util.concurrent.locks.LockSupport;

/**
 * Created by hww on 2019/1/7 4:01 PM.
 */
public abstract class MyAQS extends AbstractOwnableSynchronizer implements Serializable {

    private static final long serialVersionUID = -3198618845164580306L;

    protected MyAQS() {}

    private Node head;

    private Node tail;

    private int state;


    private boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        for (;;) {
            Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
                interrupted = true;
            }

        }
    }

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) {
            return true;
        }

        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, node.SIGNAL);
        }
        return false;
    }

    /**
     * 取消当前节点 相当于从队列中移除当前节点
     * @param node
     */
    private void cancelAcquire(Node node) {
        // 当前节点为空 则直接返回
        if (node == null) {
            return;
        }

        node.thread = null;

        // 提取当前节点的前驱
        Node pred = node.prev;
        while (pred.waitStatus > 0) {
            // 若前驱节点状态为取消 则继续往前找一个节点
            node.next = pred = pred.prev;
        }
        // 跳出while循环后  pred为有效的节点，并且链接成node的前驱

        // 提取有效前驱节点的next节点，用与后续cas修改
        Node predNext = pred.next;

        // 将当前节点状态改为取消
        node.waitStatus = Node.CANCELLED;
        if (node == tail && compareAndSetTail(node, pred)) {
            // 如果当前节点是尾节点，则将上面找到的有效前驱设置为尾节点
            // 将有效前驱的后驱设置为null
            compareAndSetNext(pred, predNext, null);
        } else {

            int ws = pred.waitStatus;

            if (pred != head
                    && (ws == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL)))
                    && pred.thread != null) {
                // 有效前驱节点不是头节点  且状态已经是或者能改成SIGNAL状态  且线程不为空
                Node next = node.next;
                if (next != null && next.waitStatus <= 0) {
                    // 提取当前节点的后继节点 若后继节点为有效节点 则链接到当前节点上
                    compareAndSetNext(pred, predNext, next);
                }
            } else {
                // 唤醒后继节点
                unparkSuccessor(node);
            }
            node.next = node;
        }
    }

    /**
     * 唤醒后继节点
     * @param node
     */
    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;

        // 当前节点可唤醒 设置为0
        if (ws < 0) {
            compareAndSetWaitStatus(node, ws, 0);
        }

        // 提取后继节点
        Node s = node.next;

        // 若后继节点不是有效节点 则从尾节点开始向前遍历 直到遍历到前驱为空，或者当前节点  即找到排在最前面的有效节点
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = tail.prev) {
                if (t.waitStatus <= 0) {
                    s = t;
                }
            }
        }

        // 有效节点不为空 就唤醒他
        if (s != null) {
            LockSupport.unpark(s.thread);
        }
    }

    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

    abstract boolean tryAcquire(int arg);

    /**
     * 将当前线程分装成一个指定占用模式的等待对象 加入队列的尾部
     */
    private Node addWaiter(Node mode) {
        // 分装当前线程到node对象，指定其占用类型
        Node node = new Node(Thread.currentThread(), mode);
        /*
         尝试一次加入队尾操作 如果不行 就调用enq方法进行CAS操作 直到成功加入到队尾位置
         之所以在此处写一遍(enq里面也有几乎一样的逻辑)，从网上找到的说法是，此部分逻辑是最有可能执行的部分，且成功几率很大
         所以此处先尝试一次，这样写可以节省指令，提高效率。
         https://www.zhihu.com/question/65591522/answer/232685841
         */
        Node t = tail;
        if (t != null) {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return node;
            }
        }
        // 上边尝试加入队尾失败 则执行enq方法直到成功将节点加入到队尾为止
        enq(node);
        return node;
    }

    /**
     * 将一个节点放入队列末尾
     * 阻塞方法
     */
    private void enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) {
                if (compareAndSetHead(new Node())) {
                    head = tail = node;
                }
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return;
                }
            }
        }
    }

    static final class Node {
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;

        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null) {
                throw new NullPointerException();
            } else {
                return p;
            }
        }

        Node() {}

        Node(Thread thread, Node mode) {
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) {
            this.thread = thread;
            this.waitStatus = waitStatus;
        }
    }

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                    (MyAQS.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                    (MyAQS.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                    (MyAQS.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }

    /**
     * CAS head field. Used only by enq.
     */
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

    /**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

    /**
     * CAS waitStatus field of a node.
     */
    private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                expect, update);
    }

    /**
     * CAS next field of a node.
     */
    private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }

}